package com.pan.kang.support;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.dom4j.tree.DefaultCDATA;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.xml.sax.InputSource;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


import org.springframework.orm.jpa.vendor.Database;
import org.xml.sax.InputSource;

public class SqlResource {
    private static final String ID_ATTR = "id";
    private static final String CLASS_NODE = "clazz";
    private static final String SQL_NODE = "sql";
    private static final String CODE_NODE = "code";
    private static final String DBTYPE_ATTR = "dbtype";
    private static final String DBTYPE_COMMON = "common";
    private static final String SQL_ID_HOLDER = "{@";
    private static final byte STATUS_UNLOAD = -1;
    private static final byte STATUS_LOADING = 0;
    private static final byte STATUS_LOADED = 1;
    private static final String ALL_CLASS = "**";
    public static final String PRELOAD_PROP = "necp.sqlres.preload";
    public static final String DEBUG_PROP = "necp.sqlres.debug";
    public static final String LOG_PROP = "necp.sqlres.log";
    private static volatile byte loadStatus = -1;
    private static final int DEFAULT_CLASS_SIZE = 100;
    private static final int DEFAULT_SQL_SIZE = 3;
    private static final int WAIT_TIME = 10000000;
    private static String dbTypeName = "common";
    private static Map<String, Map<String, String>> sqlmap = new ConcurrentHashMap(100);
    private static final String DRIVER_NAME = "spring.datasource.inner.driver-class-name";

    public SqlResource() {
    }


    static Map<String, Map<String, String>> getSqlMapForUnitTest() {
        return sqlmap;
    }

    private static Map<String, String> putClassMap(String clazz, Map<String, String> newClassMap) {
        Map<String, String> oriClassMap = (Map)sqlmap.get(clazz);
        if (oriClassMap == null) {
            Map<String, String> oriMap = (Map)sqlmap.put(clazz, newClassMap);
            if (oriMap != null) {


                return oriMap;
            } else {
                return newClassMap;
            }
        } else {


            synchronized(oriClassMap) {
                oriClassMap.putAll(newClassMap);
                return oriClassMap;
            }
        }
    }

    private static void handleSqlRef(Map<String, String> classMap) {
        Iterator<Map.Entry<String, String>> iterator = classMap.entrySet().iterator();

        while(iterator.hasNext()) {
            Map.Entry<String, String> entry = (Map.Entry)iterator.next();
            StringBuilder newSql = new StringBuilder((String)entry.getValue());
            int pos = newSql.indexOf("{@", 0);
            int count = 0;

            while(pos >= 0) {
                int end = newSql.indexOf("}", pos);
                String holderId = newSql.substring(pos + "{@".length(), end);
                if (holderId.equals(entry.getKey())) {
                }

                String holderValue = (String)classMap.get(holderId);
                if (holderValue != null) {
                    newSql.replace(pos, end + 1, holderValue);
                    pos = newSql.indexOf("{@", 0 + holderValue.length());
                    ++count;
                } else {
                    pos = newSql.indexOf("{@", end);
                }
            }

            if (count > 0) {
                classMap.put(entry.getKey(), newSql.toString());
            }
        }

    }

    private static void loadSqlCode(Map<String, String> classMap, String sqlId, Element sql) {
        Iterator<Element> codeIterator = sql.elementIterator();

        while(true) {
            while(true) {
                Element code;
                do {
                    if (!codeIterator.hasNext()) {
                        return;
                    }

                    code = (Element)codeIterator.next();
                } while(!code.getName().equals("code"));

                String dbTypeAttr = code.attributeValue("dbtype");
                if (dbTypeAttr == null) {

                } else if ("common".equals(dbTypeAttr) || dbTypeName.equals(dbTypeAttr)) {
                    Iterator<Node> cdDataIterator = code.nodeIterator();
                    String sqlCode = null;

                    while(cdDataIterator.hasNext()) {
                        Node cddataNode = (Node)cdDataIterator.next();
                        if (cddataNode instanceof DefaultCDATA) {
                            sqlCode = ((DefaultCDATA)cddataNode).getText();
                            break;
                        }
                    }

                    String oldSqlCode = (String)classMap.put(sqlId, sqlCode);

                }
            }
        }
    }

    private static String loadClassSql(String expectClass, Element clazz, Map<String, String> classMap) {
        String classId = clazz.attributeValue("id");
        if (expectClass != "**" && !expectClass.equals(classId)) {
            return null;
        } else {
            Iterator<Element> sqlIterator = clazz.elementIterator();

            while(sqlIterator.hasNext()) {
                Element sql = (Element)sqlIterator.next();
                if ("sql".equals(sql.getName())) {
                    String sqlId = sql.attributeValue("id");
                    if (sqlId != null) {
                        loadSqlCode(classMap, sqlId, sql);
                    }
                }
            }

            if (classMap.size() > 0) {
                handleSqlRef(classMap);
            }

            return classId;
        }
    }

    private static String loadSqlResource(Resource resource, String expectClass, Map<String, String> classSqlMap) throws DocumentException, IOException {
        SAXReader saxReader = new SAXReader();
        Document doc = saxReader.read(new InputSource(resource.getInputStream()));
        Element clazzElement = doc.getRootElement();
        if (clazzElement != null && "clazz".equals(clazzElement.getName())) {
            String classId = loadClassSql(expectClass, clazzElement, classSqlMap);


            return classId;
        } else {
            return null;
        }
    }

    private static Map<String, String> loadSqlResources(String clazz) {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        String sqlResourePath = "classpath*:/META-INF/ecp-sql/" + clazz + ".xml";
        Resource[] resources = null;

        try {
            resources = resolver.getResources(sqlResourePath);
        } catch (IOException var11) {

        }

        if (resources == null) {
            return null;
        } else {
            Map<String, String> classMap = new LinkedHashMap(3);
            Resource[] var5 = resources;
            int var6 = resources.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Resource resource = var5[var7];

                try {


                    if (clazz == "**") {
                        classMap = new LinkedHashMap(3);
                    }

                    String classInXml = loadSqlResource(resource, clazz, classMap);
                    if (classInXml != null) {
                        putClassMap(classInXml, classMap);
                    }
                } catch (Exception var10) {

                }
            }
            return classMap;
        }
    }

    public static String getSql(String className, String sqlId, String... params) {
        String proxySql = "";
        if (proxySql != null) {
            return proxySql;
        } else {


            int doTry = 0;

            while(loadStatus < 1) {

                if (doTry++ > 3) {
                    doInit();
                }
            }

            Map<String, String> classSqls = (Map)sqlmap.get(className);
            if (classSqls == null) {


                Map<String, String> newClassMap = loadSqlResources(className);
                if (newClassMap != null) {
                    newClassMap = putClassMap(className, newClassMap);
                    classSqls = newClassMap;
                }

                if (classSqls == null) {
                    return null;
                }
            }

            String oriSql = (String)classSqls.get(sqlId);


            if (oriSql != null && params != null && params.length > 0 && params.length % 2 == 0) {
                int group = params.length / 2;
                StringBuilder sqlBuilder = new StringBuilder(oriSql);

                for(int i = group - 1; i >= 0; --i) {
                    String holder = "{@" + params[i * 2] + "}";
                    int pos = sqlBuilder.lastIndexOf(holder);
                    String value = params[i * 2 + 1];
                    if (value == null) {

                    } else {
                        while(pos > 0) {
                            int end = pos + holder.length();
                            sqlBuilder.replace(pos, end, value);
                            pos = sqlBuilder.lastIndexOf(holder, end + 1);
                        }
                    }
                }

                return sqlBuilder.toString();
            } else {
                return oriSql;
            }
        }
    }

    private void startSqlLoader() {
        Thread asynLoadThread = new Thread() {
            public void run() {
                try {
                    SqlResource.loadSqlResources("**");
                } finally {
                    SqlResource.loadStatus = 1;
                }

            }
        };
        asynLoadThread.setName("sqlresource asynLoader");
        asynLoadThread.start();
    }

    public void handleEvent(Database type, String url) {
        if (loadStatus <= -1) {
            loadStatus = 0;
            Database dbType;
            if (url != null && url.indexOf(":dm:") > 0) {
                dbType = Database.ORACLE;
            } else if (type != Database.ORACLE && type != Database.MYSQL) {


                dbType = null;
            } else {
                dbType = type;
            }


            if (dbType != null) {
                dbTypeName = dbType.name().toLowerCase();
            } else {
                dbTypeName = "common";
            }

                this.startSqlLoader();

        }
    }

    private static void doInit() {
        String drivateClass = System.getProperty("spring.datasource.inner.driver-class-name");
        if (drivateClass != null && drivateClass.trim().length() > 0) {

            if (drivateClass.contains("mysql")) {
                (new SqlResource()).handleEvent(Database.MYSQL, (String)null);
                return;
            }

            if (drivateClass.contains("oracle")) {
                (new SqlResource()).handleEvent(Database.ORACLE, (String)null);
                return;
            }

            if (drivateClass.contains("DmDriver")) {
                (new SqlResource()).handleEvent(Database.ORACLE, (String)null);
                return;
            }
        }

    }
}
